﻿// Copyright (c) 2012 - 2020 Jared Zheng <jaredz at outlook dot com>
// Buddha Framework is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//          http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
//
// Create : 2012-12-01 version 0.1 Jared Zheng <jaredz at outlook dot com>
// Update : 2018-12-05 version 3.1.5 Jared Zheng <jaredz at outlook dot com>

#include "stdafx.h"
#include "CommonServer.h"
#include "balancepacket.h"
#include "RoutineImp.h"
#include "routinedef.h"

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// CBalanceRoutine
IMPLEMENT_RTTI_CREATE( CBalanceRoutine, CObject, CObjectTraits::OBJECTT_NONE )

UInt CBalanceRoutine::OnHandle(Int nEvent, uintptr_t utData, ULLong ullParam)
{
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->OnHandle(nEvent, utData, ullParam);
    }
    return RET_OKAY;
}

UInt CBalanceRoutine::OnHandle(Int nEvent, CEventBase& EventRef, ULLong ullParam)
{
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->OnHandle(nEvent, EventRef, ullParam);
    }
    return RET_OKAY;
}

UInt CBalanceRoutine::OnHandle(Int nEvent, CStream& Stream, ULLong ullParam)
{
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->OnHandle(nEvent, Stream, ullParam);
    }
    return RET_OKAY;
}

bool CBalanceRoutine::Start(ICommonServer* pServer)
{
    m_pServer = pServer;
    if ((m_pServer != nullptr) && LoadExtConfig())
    {
        // 授权均衡的例程是否开启异步事件队列是可选操作
        m_uThreads = (UInt)m_pServer->GetConfig().GetServerValue(CServerConfig::CFG_LOAD_BALANCE);
        if (m_uThreads > 0)
        {
            m_uThreads = CPlatform::CheckThread(m_uThreads);
            LOGV_INFO(m_pServer->GetFileLog(), TF("[授权均衡例程]异步事件队列处理线程=%d, %s"), m_uThreads, (m_uThreads > 1) ? TF("启用多线程并发处理") : TF("启用单线程处理"));
        }
        m_nUpdate = 0;
        // 超时为更新周期的倍数
        SetTimeout();

        if (m_BusinessPtr != nullptr)
        {
            if (m_BusinessPtr->Start(pServer) == false) // 注意异步事件队列还未初始化
            {
                return false;
            }
        }
        if ((m_uThreads > 0) && CEventQueue::EventQueue(m_EventQueuePtr, *this, 0, (Int)m_uThreads))
        {
            m_EventQueuePtr->SetThreadEvent(PAKE_THREAD_START, PAKE_THREAD_STOP);
            return m_EventQueuePtr->Init();
        }
        return true;
    }
    return false;
}

bool CBalanceRoutine::Pause(bool bPause)
{
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->Pause(bPause);
    }
    return false;
}

bool CBalanceRoutine::Update(void)
{
    if ((m_uThreads > 0) && (m_pServer->GetServerInfo(PAKT_BALANCE)->IsMayDay() == false))
    {
        if (m_EventQueuePtr->CheckThreads(m_uTimeout))
        {
            m_pServer->GetServerInfo(PAKT_BALANCE)->MayDay();
            DEV_ERROR(TF("[授权均衡例程]事件队列线程异常, 进入异常状态"));
            LOG_ERROR(m_pServer->GetFileLog(), TF("[授权均衡例程]事件队列线程异常, 进入异常状态"));
        }
    }
    // 定时更新线程不需要检测
    // 定时更新线程异常导致不向同步管理发送心跳包, 会导致本服务器离线

    // 定时检测扩展配置文件是否有修改
    ++m_nUpdate;
    if (m_nUpdate >= m_nTime)
    {
        m_nUpdate = 0;
        LoadExtConfig(true);
    }
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->Update();
    }
    return true;
}

void CBalanceRoutine::Stop(void)
{
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->Stop();
        CString strName = m_BusinessPtr->GetRTTI().GetName();
        m_BusinessPtr->Exit();
        m_BusinessPtr = nullptr;
        CObjectTraits::Unload<ICommonRoutine>(*strName);
    }
    if (m_EventQueuePtr != nullptr)
    {
        m_EventQueuePtr->Exit();
        m_EventQueuePtr = nullptr;
    }

    m_pServer      = nullptr;
    m_nUpdate      = 0;
    m_uThreads     = 0;
    m_uTimeout     = 0;
}

bool CBalanceRoutine::SetTimeout(UInt uTimeout)
{
    if (uTimeout == 0)
    {
        m_uTimeout = m_pServer->GetConfig().GetUpdateTick() * DATAD_TIME;
    }
    else
    {
        m_uTimeout = uTimeout;
        m_uTimeout = DEF::Max<UInt>(1, m_uTimeout);
    }
    return true;
}

bool CBalanceRoutine::AddQueue(Int nEvent, uintptr_t utData, ULLong ullParam, QUEUE_TYPE eType)
{
    if (m_pServer == nullptr)
    {
        return false;
    }
    // 入异步事件队列前的检测尽量不要处理复杂功能, 需要处理的建议进队列回调处理
    // 有加锁的建议进队列回调处理
    // "ServerAck"
    // PAKE_LINKACK/PAKE_UNLINKACK, id, center
    bool bRet = false;
    if (m_BusinessPtr != nullptr)
    {
        bRet = m_BusinessPtr->AddQueue(nEvent, utData, ullParam, eType);
    }
    switch (nEvent)
    {
    case PAKE_LINKACK:    // 向管理服务器注册回复结果
    case PAKE_UNLINK:     // 向本服务器注册的注销/断开
    case PAKE_UNLINKACK:  // 向管理服务器注销/断开回复结果
    case PAKE_TCP_CLOSED: // 连接的外网端注销/断开
        {
            bRet = true;
        }
        break;
    default: {}
    }
    if (bRet && (m_uThreads > 0))
    {
        bRet = m_EventQueuePtr->Add(nEvent, utData, ullParam, eType);
    }
    return bRet;
}

bool CBalanceRoutine::AddQueue(Int nEvent, CEventBase& EventRef, ULLong ullParam, QUEUE_TYPE eType)
{
    if (m_pServer == nullptr)
    {
        return false;
    }
    // 入异步事件队列前的检测尽量不要处理复杂功能, 需要处理的建议进队列回调处理
    // 有加锁的建议进队列回调处理
    // "CommonEvent"
    // [PAKE_COMMON_BEGIN, PAKE_COMMON_END], EventRef, PTCP_PARAM
    // 均衡对外功能
    // [PAKE_BALANCE_BEGIN, PAKE_BALANCE_ACK), EventRef, PUDP_PARAM
    bool bRet = false;
    if (m_BusinessPtr != nullptr)
    {
        bRet = m_BusinessPtr->AddQueue(nEvent, EventRef, ullParam, eType);
    }
    ULLong ullSocket = ullParam;
    if (eType > QUEUE_TYPE_MASK)
    {
        CNETTraits::PTCP_PARAM pTcp = reinterpret_cast<CNETTraits::PTCP_PARAM>(ullParam);
        ullSocket = pTcp->sSocket;
    }
    switch (nEvent)
    {
    case PAKE_BALANCE_AUTHOR:
        {
            bRet = GetAuthorAddr(ullParam);
        }
        break;
    case PAKE_BALANCE_EXTERNAL:
        {
            bRet = GetExternalAddr(ullParam);
        }
        break;
    default:
        {
            if (bRet && (m_uThreads > 0))
            {
                bRet = m_EventQueuePtr->Add(nEvent, EventRef, ullSocket, (eType & QUEUE_TYPE_MASK));
            }
        }
    }
    return bRet;
}

INLINE bool CBalanceRoutine::AddQueue(Int nEvent, CStream& Stream, ULLong ullParam)
{
    if (m_pServer == nullptr)
    {
        return false;
    }
    // 入异步事件队列前的检测尽量不要处理复杂功能, 需要处理的建议进队列回调处理
    // 有加锁的建议进队列回调处理
    // "SyncInfo"---注意流对象可能是临时实例, 不能加入队列
    // PAKE_LINK/PAKE_UPDATE, Stream, center
    bool bRet = false;
    if (m_BusinessPtr != nullptr)
    {
        bRet = m_BusinessPtr->AddQueue(nEvent, Stream, ullParam);
    }
    if (bRet && (m_uThreads > 0))
    {
        bRet = m_EventQueuePtr->Add(nEvent, Stream, ullParam);
    }
    return bRet;
}

bool CBalanceRoutine::LoadExtConfig(bool bUpdate)
{
    CStringFix strPath;
    m_pServer->GetConfig().GetServerExtConfig(CServerConfig::CFG_LOAD_BALANCE, strPath);

    CFileAttr fa;
    m_pServer->GetConfig().GetFilePath().FileAttr(fa, *strPath);
    if (bUpdate)
    {
        if (fa.MTime(m_tExtConfig))
        {
            return true;
        }
        LOGV_INFO(m_pServer->GetFileLog(), TF("[授权均衡例程]定时读取文件修改时间%llX---%llX"), fa.MTime(), m_tExtConfig);
        m_tExtConfig = fa.MTime();
    }
    else
    {
        m_tExtConfig = fa.MTime();
        LOGV_INFO(m_pServer->GetFileLog(), TF("[授权均衡例程]初始化读取文件修改时间=%llX"), m_tExtConfig);
    }
    LOGV_INFO(m_pServer->GetFileLog(), TF("[授权均衡例程]ExtConfig=%s"), *strPath);

    CFileReadStream Reader;
    if (Reader.Create(*strPath))
    {
        CKVNode  KVNode;
        CKVStore KVStore;
        if (KVStore.Load(KVNode, Reader))
        {
            if (KVNode.Find(TF("Time")))
            {
                m_nTime = KVNode[TF("Time")].GetInt();
            }

            bool bRet = true;
            if ((bUpdate == false) && (KVNode.Find(TF("BusinessRoutine"))))
            {
                bRet = LoadExtBusiness(KVNode);
            }
            if (m_BusinessPtr != nullptr)
            {
                bRet = (m_BusinessPtr->OnHandle(bUpdate ? PAKE_CONFIG_UPDATE : PAKE_CONFIG_LOAD,
                                                reinterpret_cast<uintptr_t>(&KVNode),
                                                (ULLong)m_pServer) == RET_OKAY);
            }
            return bRet;
        }
        else
        {
            LOGV_WARN(m_pServer->GetFileLog(), TF("[授权均衡例程]解析ExtConfig=%s文件内容失败"), *strPath);
        }
    }
    else
    {
        LOGV_WARN(m_pServer->GetFileLog(), TF("[授权均衡例程]打开ExtConfig=%s文件不存在"), *strPath);
    }
    return true;
}

bool CBalanceRoutine::LoadExtBusiness(CKVNode& Root)
{
    CKVString strName;
    CKVString strObject;
    CKVString strModule;

    CKVNode& Routine = Root[TF("BusinessRoutine")];
    Routine[TF("Name")].GetString(strName);
    Routine[TF("Object")].GetString(strObject);
    Routine[TF("Module")].GetString(strModule);
#if (__PLATFORM_TARGET__ == PLATFORM_TARGET_LINUX)
    strModule.Insert(0, CServerConfig::LibPrefix);
#endif
    strModule += CServerConfig::LibExt;

    assert(m_BusinessPtr == nullptr);
    if (CObjectTraits::Load<ICommonRoutine>(*strName, *strModule, m_BusinessPtr, *strObject))
    {
        LOGV_INFO(m_pServer->GetFileLog(), TF("[授权均衡例程]RTTI load --- Object=%s, Name=%s, Module=%s --- %p"), *strObject, *strName, *strModule, m_BusinessPtr.Get());
        if (m_BusinessPtr->Init() == (UInt)RET_OKAY)
        {
            m_BusinessPtr->OnHandle(PAKE_CONFIG_DEFAULT, reinterpret_cast<uintptr_t>(this), (ULLong)m_pServer);
            return true;
        }
    }
    LOGV_ERROR(m_pServer->GetFileLog(), TF("[授权均衡例程]RTTI load --- Object=%s, Name=%s, Module=%s --- Failed!"), *strObject, *strName, *strModule);
    return false;
}

bool CBalanceRoutine::GetAuthorAddr(ULLong ullParam)
{
    CNETTraits::PUDP_PARAM pUdp = reinterpret_cast<CNETTraits::PUDP_PARAM>(ullParam);

    m_pServer->GetServerInfo(PAKT_BALANCE)->IncrTotal(false);
    // TODO!!! 智能解析连接同网络的授权均衡 本地网络*A% + 负载情况*B%
    SORT_RESULT& SortResult = m_pServer->GetServerSort(PAKT_AUTHOR)->GetCur();
    if (SortResult.uCount > 0)
    {
        UInt uIndex = SortResult.Index();
        CPAKAuthorAddrAck Ack;
        Ack.SetSrvId((USRVID)m_pServer->GetServerInfo(PAKT_BALANCE)->usId); // 授权均衡的id
        Ack.SetAddr(SortResult.Sort[uIndex].Addr);
        m_pServer->GetNetworkPtr()->SendTo(pUdp->sSocket, Ack, pUdp->NetAddr);
    }
    else
    {
        CTPAKHeadBase<PAKE_BALANCE_AUTHORACK, PAKT_BALANCE> Ack((UACK)ROUTINE_ERROR_NONE_AUTHOR);
        m_pServer->GetNetworkPtr()->SendTo(pUdp->sSocket, Ack, pUdp->NetAddr);
    }
    return true;
}

bool CBalanceRoutine::GetExternalAddr(ULLong ullParam)
{
    CNETTraits::PUDP_PARAM pUdp = reinterpret_cast<CNETTraits::PUDP_PARAM>(ullParam);

    m_pServer->GetServerInfo(PAKT_BALANCE)->IncrOnline();

    CPAKExternalAddrAck Ack;
    Ack.SetSrvId((USRVID)m_pServer->GetServerInfo(PAKT_BALANCE)->usId); // 授权均衡的id
    Ack.SetAddr(pUdp->NetAddr);
    m_pServer->GetNetworkPtr()->SendTo(pUdp->sSocket, Ack, pUdp->NetAddr);
    return true;
}

#if (__PLATFORM_TARGET__ == PLATFORM_TARGET_WINDOWS)
// Windows Realse版本MSVC编译器如果不强制调用静态常量, 静态常量会被优化掉
BOOL WINAPI DllMain(HMODULE hModule, ULong ulReason, void* lpReserved)
{
    UNREFERENCED_PARAMETER( lpReserved );

    if (ulReason == DLL_PROCESS_ATTACH)
    {
        ::DisableThreadLibraryCalls(hModule);
        DEV_DUMP(TF("$ Link %s.dll --- DLL_PROCESS_ATTACH"), CBalanceRoutine::ms_RTTI.GetName());
    }
    else if (ulReason == DLL_PROCESS_DETACH)
    {
        DEV_DUMP(TF("$ Unlink %s.dll --- DLL_PROCESS_DETACH"), CBalanceRoutine::ms_RTTI.GetName());
    }
    return TRUE;
}
#endif
